#include <cassert>
#include <iostream>
#include <atomic>

#include <pthread.h>
#include <unistd.h>

class Threads {
private:
  static void * cb_increase_counter(void * arg){
    // get the counter from outside
    Threads * pThis = static_cast<Threads*>(arg);
    if(pThis == nullptr) {
      printf("Error: argument is nullptr when create thread!");
      return nullptr;
    }

    pthread_t tid = pthread_self();

    while (pThis->counter().load() < 50) {
        printf("thread 0x%04x  counter: %02d\n", (unsigned int)tid & 0xffff, (pThis->counter())++); 
        sleep(0); // release cpu for next thread run
    }

    printf("thread[0x%04x] exit\n", (unsigned int)tid & 0xffff);
    return nullptr;
  }

public:  

  void start(){
    for(int i = 0; i < THREAD_NUMBERS; ++i){
      assert(0 == pthread_create(tids+i, NULL, cb_increase_counter, this));
    }
  }

  void join(){
    for(int i =0; i < THREAD_NUMBERS; ++i){
      assert(0 == pthread_join(tids[i], NULL));
    }
  }

  std::atomic_int & counter() {
    return _counter;
  }

private:
  static const int THREAD_NUMBERS = 5;

  pthread_t tids[THREAD_NUMBERS];
  std::atomic_int _counter = 0;

};

int main(){
  Threads threads;

  threads.start();
  threads.join();

  pthread_t  tid = pthread_self();
  printf("main  [0x%04x] exit \n",(unsigned int)tid & 0xffff);

  return 0;
}
